package com.green.dao.base.impl;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.sql.Clob;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.BeanUtils;
import org.springframework.util.Assert;
import com.green.common.StreamUtils;
/**
 * 不处理任何异常 出错抛出运行时异常方便spring管理事物
 * 处理Clob类型的存储 
 * 2013-5-16
 */
public class MybatisInsertClobType {
	private MybatisInsertClobTypeTestTools mybatisInsertClobTypeTestTools = new MybatisInsertClobTypeTestTools();
    private Object emptyClob;
    
    public MybatisInsertClobType(Object emptyClob){
    	this.emptyClob=emptyClob;
    }
	
	/**
	 * 保存
	 * @param sqlSession
	 * @param sqlId
	 * @param saveObj
	 * @param tableName
	 * @param idClobColumnConfig
	 * @param clobColumnConfigs
	 */
	public void save(SqlSession sqlSession, String sqlId, Object saveObj, String tableName, ClobColumnConfig idClobColumnConfig, ClobColumnConfig[] clobColumnConfigs) {
		Assert.notNull(sqlSession, "sqlSession不能为空");
		Assert.hasText(sqlId, "sqlId不能为空");
		Assert.notNull(saveObj, "saveObj需要保存的对象不能为空");
		Assert.hasText(tableName, "tableName不能为空");
		Assert.notNull(idClobColumnConfig, "定位这天记录的idClobColumnConfig的字段信息不能为空");
		Assert.notEmpty(clobColumnConfigs, "需要更新的clob的字段信息不能为空");
    	Assert.notNull(emptyClob, "emptyClob不能为空请传入具体数据库的emptyClob");

		this.preproSave(saveObj, clobColumnConfigs, idClobColumnConfig);
		this.saveObj(sqlSession, sqlId, saveObj);
		this.saveClob(sqlSession, tableName, idClobColumnConfig, clobColumnConfigs);
	}

	/**
	 * 预处理 初始化要更新的值和条件值
	 * @param saveObj
	 * @param ClobColumnConfigs
	 * @param idClobColumnConfig
	 */
	private void preproSave(Object saveObj, ClobColumnConfig[] ClobColumnConfigs, ClobColumnConfig idClobColumnConfig) {
		for (ClobColumnConfig clobColumnConfig : ClobColumnConfigs) {
			this.mybatisInsertClobTypeTestTools.setClobColumnConfigColumnValueAndSaveObjFieldValue(clobColumnConfig, saveObj,this.emptyClob);
		}
		this.mybatisInsertClobTypeTestTools.setClobColumnConfigColumnValueAndSaveObjFieldValue(idClobColumnConfig, saveObj,null);
	}
	
	/**
	 * 保存非clob类型的数据
	 * @param sqlSession
	 * @param sqlId
	 * @param saveObj
	 */
	private void saveObj(SqlSession sqlSession, String sqlId, Object saveObj) {
		sqlSession.insert(sqlId, saveObj);
		sqlSession.commit();
	}

	/**
	 * 保存clob类型数据
	 * @param sqlSession
	 * @param tableName
	 * @param idClobColumnConfig
	 * @param clobColumnConfigs
	 */
	private void saveClob(SqlSession sqlSession, String tableName, ClobColumnConfig idClobColumnConfig, final ClobColumnConfig[] clobColumnConfigs) {
		final Connection con = this.mybatisInsertClobTypeTestTools.getConnection(sqlSession);
		final SqlConfig sqlConfig = this.mybatisInsertClobTypeTestTools.createSqlConfig(tableName, idClobColumnConfig, clobColumnConfigs);
		final ProcessResultSet process = new ProcessResultSet() {
			@Override
			public void process(ResultSet resultSet) {
				try {
					if (resultSet.next()) {
                        for (ClobColumnConfig clobColumnConfig : clobColumnConfigs) {
                        	final Object one=clobColumnConfig.columnValue;
                        	final Clob target=resultSet.getClob(clobColumnConfig.getColumnName());
                        	mybatisInsertClobTypeTestTools.writeValueFromOneToTarget(one, target);
						}
					}
				} catch (SQLException e) {
					throw new RuntimeException("处理resultSet出错",e);
				}
			}
		};
		this.mybatisInsertClobTypeTestTools.execSql(con, sqlConfig, process);
	}

	/**
	 * 创建
	 * @param columnName
	 * @param saveObjFieldName
	 * @return
	 */
	public ClobColumnConfig createClobColumnConfig(String columnName, String saveObjFieldName) {
		return new ClobColumnConfig(columnName, saveObjFieldName);
	}

	/**
	 * 工具类
	 *
	 * 2013-5-16
	 */
	class MybatisInsertClobTypeTestTools {
		private final static int CLOBPOS=1;
		/**
		 * 复制 one的值到
		 * @param one
		 * @param target
		 */
		private void writeValueFromOneToTarget(Object one,Clob target){
			InputStream input=null;
			OutputStream output=null;
			try {
				 input=StreamUtils.asInputStream(one);
				 output=target.setAsciiStream(CLOBPOS);
				 StreamUtils.copy(input, output);
			} catch (Exception e) {
				throw new RuntimeException("设置clob类型出错",e);
			}finally{
				if(input!=null){
					try {
						input.close();
					} catch (IOException e) {
					}
				}
				if(output!=null){
					try {
						output.close();
					} catch (IOException e) {
					}
				}
			}
		}
		
		/**
		 * 执行查询的sql
		 * @param con
		 * @param sqlConfig
		 * @param process
		 */
		private void execSql(Connection con, SqlConfig sqlConfig, ProcessResultSet process) {
			final String sql = sqlConfig.getSql();
			final Object[] params = sqlConfig.getParams();

			PreparedStatement preparedStatement = null;
			ResultSet resultSet = null;
			try {
				preparedStatement = con.prepareStatement(sql);
				for (int i = 0; i < params.length; i++) {
					preparedStatement.setObject(i + 1, params[i]);
				}
				resultSet = preparedStatement.executeQuery();
				process.process(resultSet);
			} catch (SQLException e) {
				throw new RuntimeException("执行sql语句出错sql="+sql,e);
			}  finally {
				try {
					resultSet.close();
				} catch (SQLException e1) {
				}
				try {
					preparedStatement.close();
				} catch (SQLException e) {
				}
			}

		}

		/**
		 * 得到sqlsession的connection用于共享一个事物用
		 * @param sqlSession
		 * @return
		 */
		private Connection getConnection(SqlSession sqlSession) {
			return sqlSession.getConnection();
		}

		/**
		 * 创建要执行的sql信息
		 * @param tableName
		 * @param idClobColumnConfig
		 * @param clobColumnConfigs
		 * @return
		 */
		private SqlConfig createSqlConfig(String tableName, ClobColumnConfig idClobColumnConfig, ClobColumnConfig[] clobColumnConfigs) {
			final StringBuilder sql = new StringBuilder();

			sql.append("select ");
			for (int i = 0; i < clobColumnConfigs.length; i++) {
				sql.append(clobColumnConfigs[i].getColumnName());
				final boolean appendComma = i != clobColumnConfigs.length - 1;
				if (appendComma) {
					sql.append(",");
				}
			}
			sql.append(" from ");
			sql.append(" ").append(tableName).append(" ");
			sql.append(" where ");
			sql.append(idClobColumnConfig.getColumnName()).append("=?");
			sql.append(" for update ");
            
			final Object whereValue=idClobColumnConfig.columnValue;
			Assert.notNull(whereValue,"idClobColumnConfig对应的值不能为空");
			
			final Object[] params = new Object[] { whereValue };
			final SqlConfig sqlConfig = new SqlConfig();
			sqlConfig.setSql(sql.toString());
			sqlConfig.setParams(params);
			return sqlConfig;
		}

		/**
		 * 设置 clobColumnConfig的ColumnValue和SaveObj对应字段Value
		 * @param clobColumnConfig
		 * @param saveObj
		 * @param value
		 */
		private void setClobColumnConfigColumnValueAndSaveObjFieldValue(ClobColumnConfig clobColumnConfig,Object saveObj,Object value){
			final String saveObjFieldName = clobColumnConfig.getSaveObjFieldName();
			final Object saveObjFieldValue = this.getTargetFieldValue(saveObj, saveObjFieldName);
			clobColumnConfig.columnValue = saveObjFieldValue;
			
			final boolean isSetValue=value!=null;
			if(isSetValue){
				this.setTargetFieldValue(saveObj, saveObjFieldName,value);
			}
		}
		
		/**
		 * 取得target对象的field值   
		 * @param target
		 * @param field
		 * @return
		 */
		private Object getTargetFieldValue(Object target, String field) {
			final PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(target.getClass(), field);
			final Method fieldReadMethod = propertyDescriptor.getReadMethod();
			try {
				return fieldReadMethod.invoke(target);
			} catch (Exception e) {
				throw new RuntimeException("取得对象["+target.getClass().getName()+"."+field+"]的值出错",e);
			}
		}

		/**
		 * 设置target对象field的值
		 * @param target
		 * @param field
		 * @param value
		 */
		private void setTargetFieldValue(Object target, String field, Object value) {
			final PropertyDescriptor propertyDescriptor = BeanUtils.getPropertyDescriptor(target.getClass(), field);
			final Method fieldWriteMethod = propertyDescriptor.getWriteMethod();
			try {
				fieldWriteMethod.invoke(target, value);
			} catch (Exception e) {
				throw new RuntimeException("设置对象["+target.getClass().getName()+"."+field+"="+value+"]的值出错",e);
			}
		}

	}

	/**
	 * 回调处理返回的结果
	 *
	 * 2013-5-16
	 */
	public interface ProcessResultSet {
		void process(ResultSet resultSet);
	}

	/**
	 * sql信息
	 *
	 * 2013-5-16
	 */
	public class SqlConfig {
		private String sql;
		private Object[] params;

		public String getSql() {
			return sql;
		}

		public void setSql(String sql) {
			this.sql = sql;
		}

		public Object[] getParams() {
			return params;
		}

		public void setParams(Object[] params) {
			this.params = params;
		}


	}

	/**
	 * 数据库Column信息
	 *
	 * 2013-5-16
	 */
	public class ClobColumnConfig {
		private String columnName;
		private String saveObjFieldName;
		private Object columnValue;

		public ClobColumnConfig(String columnName, String saveObjFieldName) {
			this.columnName = columnName;
			this.saveObjFieldName = saveObjFieldName;
		}

		public String getColumnName() {
			return columnName;
		}

		public void setColumnName(String columnName) {
			this.columnName = columnName;
		}

		public String getSaveObjFieldName() {
			return saveObjFieldName;
		}

		public void setSaveObjFieldName(String saveObjFieldName) {
			this.saveObjFieldName = saveObjFieldName;
		}

	}
}
